package gamebackend;

import framework.Color;
import framework.GraphicsWrapper;
import framework.ImageTinter;
import framework.ImageWrapper;
import gui.BlockStyle;

import java.util.Random;

/**
 * Represents a Block in a BlockLayer.
 * @author Jonathan Caddey
 *
 */
public class Block {
	/**
	 * Describes the neighbor of a block, used for selecting the block's image.
	 * 
	 * @author Jonathan Caddey
	 */
	public enum NeighborState {
		/**
		 * There is and never was a block attached to this block in a particular
		 * direction.
		 */
		EMPTY,
		
		/**
		 * There currently is a block attached to this block in a particular
		 * direction.
		 */
		FULL,
		
		/**
		 * There once was a block attached to this block in a particular
		 * direction, but it is gone now.
		 */
		DESTROYED;
	}
	/**
	 * Whether a new image is created for every block.
	 */
	/*
	 * I don't like this as a public static field, but there's
	 * no reason to have different blocks with different
	 * settings of this.
	 */
	public static boolean USE_COMPOSITE;
	
	public static final Block EMPTY_BLOCK = new Block();
	/**
	 * Used for paths it's generally safe for the player to fly on.
	 */
	public static final Block PRIMARY_PATH = new Block(BlockStyle.PLAIN, Color.WHITE);
	/**
	 * Used for alternate paths that may end in dead ends, have more hazards, etc.
	 */
	public static final Block SECONDARY_PATH = new Block(BlockStyle.PLAIN, Color.WHITE);
	
	private NeighborState[] my_neighbors = new NeighborState[4];

	/**
	 * Whether my_image needs to be redrawn.
	 */
	private boolean my_imageIsDirty = true;
	
	
	private BlockStyle my_style;
	/**
	 * Makes up the four corners of a block--top
	 * left, top right, bottom right, bottom left.
	 */
	private ImageWrapper[] my_images = new ImageWrapper[4];
	/**
	 * A composite of the 4 corners.
	 */
	private ImageWrapper my_image;
	private Color my_color;
	private ImageTinter my_tinter;
	public Block() {
	};
	public Block(BlockStyle style, Color color) {
		this();
		setStyle(style);
		setColor(color);
	}
	
	
	public static Color getRandomColor(final Random the_random, final int the_totalComponent, final int the_minComponent) {
		final int[] part = new int[3];
		int colorLeft = the_totalComponent;
		for (int i = 0; i < 3; i++) {
			int min = Math.max(the_minComponent, colorLeft - (2 - i) * 255);
			int max = colorLeft - (2 - i) * the_minComponent;
			part[i] = min + the_random.nextInt(Math.max(1, Math.min(max - min, 255 - min)));
			colorLeft -= part[i];
		}
		if (part[0] + part[1] + part[2] != the_totalComponent) {
			System.out.println(part[0] + " + " + part[1] + " + " + part[2] + " = " + (part[0] + part[1] + part[2]));
		}
		for (int i = 1; i < 3; i++) {
			int swap = the_random.nextInt(i + 1);
			if (swap != i) {
				final int temp = part[swap];
				part[swap] = part[i];
				part[i] = temp;
			}
		}
		return new Color(part[0], part[1], part[2]);
	}
	public static Color getBackgroundColor(Color the_color) {
		final float factor = 65f / 255;
		return new Color((int)(the_color.getRed() * factor),
				(int)(the_color.getGreen() * factor),
				(int)(the_color.getBlue() * factor));
	}
	
	
	/**
	 * @param block
	 * @return whether the given block is neither empty nor a path.
	 */
	public static boolean isOccupied(Block block) {
		return block != EMPTY_BLOCK && !block.isPath();
	}
	public boolean isOccupied() {
		return isOccupied(this);
	}
	public boolean isPath() {
		return this == PRIMARY_PATH || this == SECONDARY_PATH;
	}
	public boolean canJoin(Block other) {
		return other.getStyle() == getStyle() && other.getColor().equals(getColor());
	}
	public void setStyle(final BlockStyle the_style) {
		my_style = the_style;
	}
	public BlockStyle getStyle() {
		return my_style;
	}
	
	public void setImages(ImageWrapper[] the_images) {
		my_images = the_images;
	}
	public void setColor(final Color the_color) {
		my_tinter = null;
		my_color = the_color;
	}
	public void setTinter(final ImageTinter the_tinter) {
		my_tinter = the_tinter;
	}
	public ImageTinter getTinter() {
		return my_tinter;
	}
	public Color getColor() {
		return my_color;
	}
	public void markImageAsDirty() {
		my_imageIsDirty = true;
	}
	
	
	public void render(GraphicsWrapper g, int x, int y, int width, int height) {
		g.saveMatrix();
		if (my_tinter == null) {
			my_tinter = g.newImageTinter(my_color);
		}
		if (my_images != null && my_images[0] != null) {
			if (USE_COMPOSITE) {
				final int uscale = 3;
				
				if (my_imageIsDirty) {
					my_imageIsDirty = false;
					if (my_image == null) {
						my_image = g.newImage(width * uscale,
								height * uscale);
					}
					GraphicsWrapper img = my_image.getGraphics();
					img.clear();
//					img.fillRect(0, 0, my_image.getWidth(), my_image.getHeight());
//					img.setColor(Color.RED);
					img.scale(uscale, uscale);
					img.scale(.5 * (width + 1) / my_images[0].getWidth(),
							.5 * (height + 1) / my_images[0].getHeight());
					my_tinter.drawTintedImage(img, my_images[0], 0, 0);
					img.translate(2 * my_images[0].getWidth() - 1, 0);
					img.scale(-1, 1);
					my_tinter.drawTintedImage(img, my_images[1], 0, 0);
					img.translate(0, 2 * my_images[0].getHeight() - 1);
					img.scale(1, -1);
					my_tinter.drawTintedImage(img, my_images[2], 0, 0);
					img.translate(2 * my_images[0].getWidth(), 0);
					img.scale(-1, 1);
					my_tinter.drawTintedImage(img, my_images[3], 0, 0);
					img.dispose();
					
					
				}
				g.translate(x, y);
				g.scale(1f / uscale, 1f / uscale);
				g.drawImage(my_image, 0, 0);
			
			} else {
				g.translate(x, y);
				g.scale(.5 * (width + 1) / my_images[0].getWidth(),
						.5 * (height + 1) / my_images[0].getHeight());
				my_tinter.drawTintedImage(g, my_images[0], 0, 0);
				g.translate(2 * my_images[0].getWidth() - 1, 0);
				g.scale(-1, 1);
				my_tinter.drawTintedImage(g, my_images[1], 0, 0);
				g.translate(0, 2 * my_images[0].getHeight() - 1);
				g.scale(1, -1);
				my_tinter.drawTintedImage(g, my_images[2], 0, 0);
				g.translate(2 * my_images[0].getWidth() - 1, 0);
				g.scale(-1, 1);
				my_tinter.drawTintedImage(g, my_images[3], 0, 0);
			}
			/*
			g.translate(x, y);
			g.scale(.5 * (width + 2) / my_images[0].getWidth(),
					.5 * (height + 2) / my_images[0].getHeight());
			g.drawImage(my_images[0], my_rop, 0, 0);
			g.translate(2 * my_images[0].getWidth() - 1, 0);
			g.scale(-1, 1);
			g.drawImage(my_images[1], my_rop, 0, 0);
			g.translate(0, 2 * my_images[0].getHeight());
			g.scale(1, -1);
			g.drawImage(my_images[2], my_rop, 0, 0);
			g.translate(2 * my_images[0].getWidth() - 1, 0);
			g.scale(-1, 1);
			g.drawImage(my_images[3], my_rop, 0, 0);
			 */
			
			
		} else {
			g.setColor(my_color);
			g.fillRect(x, y, width, height);
		}
		
//		if (my_image != null) {
//			g.translate(x, y);
//			g.scale((float)width / my_image.getWidth(), (float)height / my_image.getHeight());
//			g.drawImage(my_image, 0, 0, null);
//		} TODO why was this ever here?
		g.restoreMatrix();
	}
	
	public void setNeighbors(NeighborState top, NeighborState right, NeighborState bot, NeighborState left) {
		my_neighbors[0] = top;
		my_neighbors[1] = right;
		my_neighbors[2] = bot;
		my_neighbors[3] = left;
	}
	public void setNeighborState(final int the_direction, final NeighborState the_state) {
		my_neighbors[the_direction] = the_state;
		// TODO some kind of dirty here?
	}
	
	public NeighborState getNeighborState(int direction) {
		return my_neighbors[direction];
	}
	public void dispose() {
		if (my_image != null) {
			my_image.dispose();
		}
		// this would dispose the images from blockstyles themselves.
//		if (my_images != null && my_images[0] != null) {
//			for (int i = 0; i < my_images.length; i++) {
//				my_images[i].dispose();
//			}
//		}
	}
	
	
	
	

}
